package team.se.tms.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import team.se.tms.shiro.CredentialsMatcher;
import team.se.tms.shiro.ShiroRealm;

import java.util.HashMap;
import java.util.Map;

/**
 * shiro的配置类
 *
 * @author winkyle
 * @date 2018/11/12
 */
@Slf4j
@Configuration
public class ShiroConfig {

    /**
     * 得到LifecycleBeanPostProcessor，通过@Bean注入配置
     * 负责bean的生命周期的，初始化和销毁。
     *
     * @return LifecycleBeanPostProcessor
     * @create on 2018/11/17 by winkyle
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 得到EhCacheManager，通过@Bean注入配置
     * EhCacheManager，缓存管理，用户登陆成功后，把用户信息和权限信息缓存起来，
     * 然后每次用户请求时，从用户放入的session中获取，如果不设置这个bean，每个请求都会查询一次数据库。
     *
     * @return EhCacheManager
     * @create on 2018/11/17 by winkyle
     */
    @Bean(name = "ehCacheManager")
    @DependsOn(value = {"lifecycleBeanPostProcessor"})
    public EhCacheManager getEhCacheManager() {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        return ehCacheManager;
    }

    /**
     * 得到CredentialsMatcher，通过@Bean注入配置
     *
     * @return CredentialsMatcher
     * @create on 2018/11/17 by winkyle
     */
    @Bean(name = "credentialsMatcher")
    @DependsOn(value = {"lifecycleBeanPostProcessor"})
    public CredentialsMatcher getCredentialsMatcher(){
        return new CredentialsMatcher();
    }

    /**
     * 得到ShiroRealm，通过@Bean注入配置
     *
     * @param credentialsMatcher 通过查找注入的Bean配置获得CredentialsMatcher类
     * @param ehCacheManager 通过查找注入的Bean配置获得EhCacheManager类
     * @return ShiroRealm
     * @create on 2018/11/12 by winkyle
     */
    @Bean(name = "shiroRealm")
    @DependsOn(value = {"credentialsMatcher","ehCacheManager"})
    public ShiroRealm getShiroRealm(CredentialsMatcher credentialsMatcher,EhCacheManager ehCacheManager) {
        ShiroRealm shiroRealm = new ShiroRealm();
        shiroRealm.setCredentialsMatcher(credentialsMatcher);
        shiroRealm.setCacheManager(ehCacheManager);
        return shiroRealm;
    }

    /**
     * 得到SecurityManager，通过@Bean注入配置
     *
     * @param shiroRealm 通过查找注入的Bean配置获得ShiroRealm类
     * @return SecurityManager
     * @create on 2018/11/12 by winkyle
     */
    @Bean(name = "securityManager")
    @DependsOn(value = {"shiroRealm"})
    public SecurityManager getSecurityManager(ShiroRealm shiroRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(shiroRealm);
        return securityManager;
    }

    /**
     * 获取ShiroFilterFactoryBean，通过@Bean注入配置
     *
     * @param securityManager 通过查找注入的Bean配置获得SecurityManager类
     * @return ShiroFilterFactoryBean
     * @create on 2018/11/12 by winkyle
     */
    @Bean(name = "shiroFilterFactoryBean")
    @DependsOn(value = {"securityManager"})
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //配置未登录访问的url
        shiroFilterFactoryBean.setLoginUrl("/user/to_login");
        //配置验证通过访问的url
        shiroFilterFactoryBean.setSuccessUrl("/menu/main/**");
        //配置验证不通过访问的url
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        Map<String,String> filterChainDefinitionMap = new HashMap<>();
        //配置无需验证即可访问的url
        filterChainDefinitionMap.put("/code/verification_code","anon");
        filterChainDefinitionMap.put("/user/login","anon");
        filterChainDefinitionMap.put("/static/**","anon");
        //配置登出访问的url
        filterChainDefinitionMap.put("/user/logout", "logout");
        //配置认证后才可访问的url
        filterChainDefinitionMap.put("/**","authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 得到DefaultAdvisorAutoProxyCreator，通过@Bean注入配置
     * DefaultAdvisorAutoProxyCreator，Spring的一个bean，由Advisor决定对哪些类的方法进行AOP代理。自动创建代理
     *
     * @return DefaultAdvisorAutoProxyCreator
     * @create on 2018/11/17 by winkyle
     */
    @Bean
    @DependsOn(value = {"lifecycleBeanPostProcessor"})
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        defaultAAP.setUsePrefix(true);
        return defaultAAP;
    }

    /**
     * 得到AuthorizationAttributeSourceAdvisor，通过@Bean注入配置
     * AuthorizationAttributeSourceAdvisor，shiro里实现的Advisor类，
     * 内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。
     * 开启shiro aop注解支持.
     * 使用代理方式;所以需要开启代码支持;否则@RequiresRoles等注解无法生效
     *
     * @param securityManager 通过查找注入的Bean配置获得SecurityManager类
     * @return AuthorizationAttributeSourceAdvisor
     * @create on 2018/11/17 by winkyle
     */
    @Bean
    @DependsOn(value = {"securityManager"})
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
